/**
 * Yolema.com Inc.
 * Copyright (c) 2011-2012 All Rights Reserved.
 */
package org.grayrabbit.pay.core.biz.manager.interceptor;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.grayrabbit.pay.core.model.exception.PayException;
import org.grayrabbit.pay.core.model.results.PayResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 异常处理拦截器
 * 
 * @author liyd
 * @version $Id: ExceptionAspect.java, v 0.1 2012-4-6 下午5:24:39 liyd Exp $
 */
@Aspect
@Component
public class ExceptionInterceptor {

    /** 日志对象 */
    private static final Logger LOG = LoggerFactory.getLogger(ExceptionInterceptor.class);

    /**
     * 拦截manager层方法，处理异常
     * <p>
     * 如果业务层方法返回的是指定的规范的result，封装处理返回结果，否则不处理
     * 
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("execution(* org.grayrabbit.pay.core.biz.manager.impl.*.*(..))")
    public Object aroundAspect(ProceedingJoinPoint pjp) throws Throwable {

        //当前时间毫秒数
        Long now = System.currentTimeMillis();

        if (LOG.isInfoEnabled()) {
            LOG.info("开始执行业务层方法[" + pjp.getTarget().getClass() + ",method="
                     + pjp.getSignature().getName() + "]");
        }
        Object result = null;

        try {

            //此处调用业务方法，可以传入参数，将覆盖方法原有的参数
            result = pjp.proceed();

        } catch (PayException payException) {

            //该异常为已知的处理异常，因此只输出简化处理过的结果码和结果信息
            LOG.warn("出现已知异常，异常信息[resultCode=" + payException.getResultCode() + ",resultMsg="
                     + payException.getResultMsg() + "]");

            //设置返回结果
            Object obj = getResult(pjp, payException);

            if (obj != null) {

                return obj;

            }

        } catch (Exception e) {

            LOG.warn("发现未知异常，异常信息", e);

            //设置返回结果
            Object obj = getResult(pjp, null);

            if (obj != null) {

                return obj;

            }
        } finally {

            if (LOG.isInfoEnabled()) {
                LOG.info("业务层方法执行完毕[" + pjp.getTarget().getClass() + ",method="
                         + pjp.getSignature().getName() + "]，耗时["
                         + (System.currentTimeMillis() - now) + "ms]");
            }
        }

        return result;
    }

    /**
     * 设置并返回业务方法的返回结果
     * 
     * @param pjp spring拦截业务对象
     * @param payException 已知异常信息
     * @return
     */
    private Object getResult(ProceedingJoinPoint pjp, PayException payException) {

        try {
            //得到业务方法原有的参数
            Object[] args = pjp.getArgs();

            //存放业务方法参数类型
            Class<?>[] paramsTypes = new Class<?>[args.length];

            //将业务方法的参数类型存放到数组
            for (int i = 0; i < args.length; i++) {

                paramsTypes[i] = args[i].getClass();

            }

            //得到业务方法
            Method method = pjp.getTarget().getClass()
                .getMethod(pjp.getSignature().getName(), paramsTypes);

            //得到业务方法的返回类型
            Class<?> clazz = method.getReturnType();

            //初始化一个业务方法的返回类型
            Object obj = Class.forName(clazz.getName()).newInstance();

            //如果是指定的业务返回类型，设置返回结果
            if (obj instanceof PayResult) {

                ((PayResult) obj).setSuccess(false);

                if (payException != null) {
                    ((PayResult) obj).setResultCode(payException.getResultCode());
                    ((PayResult) obj).setResultMsg(payException.getResultMsg());
                }

            }

            return obj;

        } catch (NoSuchMethodException e) {
            LOG.error("业务方法不存在", e);
        } catch (InstantiationException e) {
            LOG.error("初始化业务方法返回值失败", e);
        } catch (IllegalAccessException e) {
            LOG.error("利用反射初始化业务方法返回值失败", e);
        } catch (ClassNotFoundException e) {
            LOG.error("业务类不存在", e);
        }

        return null;

    }

}
